/*
 * nodes.c
 *
 *  Created on: Jan 15, 2014
 *      Author: univ3rse
 */

#include "nodes.h"
#include <stdio.h>
#include <stdlib.h>

volatile static scopeLevel = 0;

void startScope() {
	scopeLevel++;
}

void endScope() {
	if(scopeLevel > 0) {
		removeAllSymbolsFromScopeLevel(scopeLevel);
		scopeLevel--;
		//printf("endScope=%d\n", scopeLevel);
	}
}

expTree opExp(oType op, expTree left, expTree right) {
	expTree returnVal = (expTree) malloc(sizeof(struct exp));
	returnVal->expKind = oExp;
	returnVal->uni.oper.op = op;
	returnVal->uni.oper.left = left;
	returnVal->uni.oper.right = right;
	return returnVal;
}

expTree relopExp(reloType relo, expTree left, expTree right) {
	expTree returnVal = (expTree) malloc(sizeof(struct exp));
	returnVal->expKind = rExp;
	returnVal->uni.relo.op = relo;
	returnVal->uni.relo.left = left;
	returnVal->uni.relo.right = right;
	return returnVal;
}

expTree boolExp(boolType boolop, expTree left, expTree right) {
	expTree returnVal = (expTree) malloc(sizeof(struct exp));
	returnVal->expKind = bExp;
	returnVal->uni.boolo.op = boolop;
	returnVal->uni.boolo.left = left;
	returnVal->uni.boolo.right = right;
	return returnVal;
}

expTree idExp(char *var) {
	expTree returnVal = (expTree) malloc(sizeof(struct exp));
	returnVal->expKind = vExp;
	returnVal->uni.var = var;
	return returnVal;
}

expTree conExp(conType ds, int constVal) {
	expTree returnVal = (expTree) malloc(sizeof(struct exp));
	returnVal->expKind = cExp;
	returnVal->uni.val.ds = ds;
	returnVal->uni.val.constVal = constVal;
	return returnVal;
}

expTree ifsExp(ifType it, expTree relEx, expTree ifSt, expTree elseSt) {
	expTree returnVal = (expTree) malloc(sizeof(struct exp));
	returnVal->expKind = ifExp;
	returnVal->uni.ifs.it = it;
	returnVal->uni.ifs.relEx = relEx;
	returnVal->uni.ifs.ifSt = ifSt;
	returnVal->uni.ifs.elseSt = elseSt;
	return returnVal;
}

expTree loopsExp(loopType lp, expTree relEx, expTree loopSt) {
	expTree returnVal = (expTree) malloc(sizeof(struct exp));
	returnVal->expKind = loopExp;
	returnVal->uni.loops.lp = lp;
	returnVal->uni.loops.relEx = relEx;
	returnVal->uni.loops.loopSt = loopSt;
	return returnVal;
}

expTree multiStmt(expTree left, expTree right) {
	expTree returnVal = (expTree) malloc(sizeof(struct exp));
	returnVal->expKind = multiExp;
	returnVal->uni.multi.left = left;
	returnVal->uni.multi.right = right;
	return returnVal;
}

expTree printExp(expTree toPrint) {
	expTree returnVal = (expTree) malloc(sizeof(struct exp));
	returnVal->expKind = prExp;
	returnVal->uni.print.toPrint = toPrint;
	return returnVal;
}

data_t ex(expTree eT) {
	data_t result;
	data_t booleanEval;
	/*printf("ex run\n");*/
	if (!eT) {
		printf("not et\n");
		result.type = INT32;
		result.value.int32 = 0;
		return result;
	}
	switch (eT->expKind) {

	case multiExp:
		ex(eT->uni.multi.left);
		return ex(eT->uni.multi.right);

	case cExp:

		switch (eT->uni.val.ds) {
		case IN: {
			/* HACK: Casting of constVal. */
			result.type = INT32;
			result.value.int32 = (int)eT->uni.val.constVal;
			return result;
		}
		case FN: {
			result.type = FP32;
			result.value.fp32 = eT->uni.val.constVal;
			return result;
		}
		default:
			printf("no valid ds %d\n", eT->uni.val.ds);
			result.type = INT32;
			result.value.int32 = -1;
			return result;
		}
		break;

	case vExp:
		return getValue(eT->uni.var, scopeLevel);

	case oExp:
		switch (eT->uni.oper.op) {

		case ASI:
			switch (eT->uni.oper.left->expKind) {

			case vExp:
				setValue(eT->uni.oper.left->uni.var, scopeLevel, ex(eT->uni.oper.right));
				result.type = INT32;
				result.value.int32 = 0;
				return result;
			case oExp:
				setValue(eT->uni.oper.left->uni.oper.left->uni.var, scopeLevel, ex(eT->uni.oper.right));
				result.type = INT32;
				result.value.int32 = 0;
				return result;
			}
			result.type = INT32;
			result.value.int32 = 0;
			return result;
			/*	case UMINUS:    return -ex(p->opr.op[0]);*/
		case ADD:
			//return ex(eT->uni.oper.left) + ex(eT->uni.oper.right);
			return add(ex(eT->uni.oper.left), ex(eT->uni.oper.right));
		case SUB:
			return sub(ex(eT->uni.oper.left), ex(eT->uni.oper.right));
		case MUL:
			return mul(ex(eT->uni.oper.left), ex(eT->uni.oper.right));
		case DIV:
			return divide(ex(eT->uni.oper.left), ex(eT->uni.oper.right));
		case FCT:
			return factorial(ex(eT->uni.oper.left));
		case DEC:
			switch (eT->uni.oper.left->expKind) {
			case cExp:
				return decrement(ex(eT->uni.oper.left));
			case vExp:
				setValue(eT->uni.oper.left->uni.var, scopeLevel,
						decrement(ex(eT->uni.oper.left)));
				result.type = INT32;
				result.value.int32 = 0;
				return result;
			}
			printf("DEC problem\n");
			break;

		case INC:
			switch (eT->uni.oper.left->expKind) {
			case cExp:
				return increment(ex(eT->uni.oper.left));
			case vExp:
				setValue(eT->uni.oper.left->uni.var, scopeLevel,
						increment(ex(eT->uni.oper.left)));
				result.type = INT32;
				result.value.int32 = 0;
				return result;
			}
			printf("INC problem\n");
			break;
		default:
			printf("OP not recognized");
			result.type = INT32;
			result.value.int32 = -1;
			return result;
		}
		break;

	case ifExp:
		booleanEval = ex(eT->uni.ifs.relEx);
		if (booleanEval.value.int32) {
			startScope();
			ex(eT->uni.ifs.ifSt);
			endScope();
		} else if (eT->uni.ifs.it == elseSt) {
			startScope();
			ex(eT->uni.ifs.elseSt);
			endScope();
		}
		result.type = INT32;
		result.value.int32 = 0;
		return result;

/*	case ifExp:
		booleanEval = ex(eT->uni.ifs.relEx);
		if (booleanEval.value.int32)
			ex(eT->uni.ifs.ifSt);
		else if (eT->uni.ifs.it == elseSt)
			ex(eT->uni.ifs.elseSt);
		result.type = INT32;
		result.value.int32 = 0;
		return result;
*/
	case loopExp:
		switch (eT->uni.loops.lp) {

		case whileLoop:
			startScope();
			booleanEval = ex(eT->uni.loops.relEx);
			while (booleanEval.value.int32) {
				ex(eT->uni.loops.loopSt);
				booleanEval = ex(eT->uni.loops.relEx);
			}
			endScope();
			result.type = INT32;
			result.value.int32 = 0;
			return result;

		case untilLoop:
			startScope();
			booleanEval = ex(eT->uni.loops.relEx);
			while (!(booleanEval.value.int32))
				ex(eT->uni.loops.loopSt);
				booleanEval = ex(eT->uni.loops.relEx);
			endScope();
			result.type = INT32;
			result.value.int32 = 0;
			return result;
		}
		break;

	case rExp:
		switch (eT->uni.oper.op) {
		case lt:
			//return ex(eT->uni.oper.left) < ex(eT->uni.oper.right);
			return lessThan(ex(eT->uni.oper.left), ex(eT->uni.oper.right));
		case gt:
			return greaterThan(ex(eT->uni.oper.left), ex(eT->uni.oper.right));
		case ge:
			return greaterThanEqual(ex(eT->uni.oper.left), ex(eT->uni.oper.right));
		case le:
			return lessThanEqual(ex(eT->uni.oper.left), ex(eT->uni.oper.right));
		case ne:
			return notEqual(ex(eT->uni.oper.left), ex(eT->uni.oper.right));
		case eq:
			return equal(ex(eT->uni.oper.left), ex(eT->uni.oper.right));
		}
		printf("Relational problem");
		break;

	case bExp:
		switch (eT->uni.oper.op) {
		case and:
			return lAnd(ex(eT->uni.oper.left), ex(eT->uni.oper.right));
		case or:
			return lOr(ex(eT->uni.oper.left), ex(eT->uni.oper.right));
		}
		printf("Boolean problem");
		break;

	case prExp:
		printValue(ex(eT->uni.print.toPrint));
		result.type = INT32;
		result.value.int32 = 0;
		return result;

	default:
		printf("Not valid switch\n");
		result.type = INT32;
		result.value.int32 = -1;
		return result;

	}
	printf("no switch\n");
	result.type = INT32;
	result.value.int32 = 0;
	return result;

}
